#ifndef _SELECT_HPP_
#define _SELECT_HPP_

#include <iostream>
#include <string>
#include <sys/select.h>
#include <sys/time.h>
#include "log.hpp"
#include "Sock.hpp"

using std::cout;
using std::endl;
using std::string;

#define BITS 8
#define NUM (sizeof(fd_set) * BITS)
#define FD_NOME -1

class SelectServer
{
public:
    SelectServer(const uint16_t &port = 8080)
        : _port(port)
    {
        _listensock = Sock::Socket();
        Sock::Bind(_listensock, _port);
        Sock::Listen(_listensock);
        logMessage(DEBUG, "%s", "create base socket success");
        for (int i = 0; i < NUM; i++)
        {
            _fd_array[i] = FD_NOME;
        }

        _fd_array[0] = _listensock; // 规定 _fd_array[0] = _listensock;
    }

    void start()
    {
        while (true)
        {
            DebugPrint(); // _listensock 获取连接 一样也是IO

            fd_set rfds;
            FD_ZERO(&rfds); // 先清空
            int maxfd = _listensock;
            for (int i = 0; i < NUM; i++)
            {
                if (_fd_array[i] == FD_NOME)
                    continue;
                FD_SET(_fd_array[i], &rfds);
                if (maxfd < _fd_array[i])
                    maxfd = _fd_array[i];
            }

            // 未来的rfds中,一定会有两类sock, listensock 普通sock
            int n = select(maxfd + 1, &rfds, nullptr, nullptr, nullptr);
            switch (n)
            {
            case 0:
                logMessage(DEBUG, "%s", "time out");
                break;
            case -1:
                logMessage(WARNING, "select error: %d : %s", errno, strerror(errno));
                break;
            default:
                logMessage(DEBUG, "get a new link ...");
                HandlerEvent(rfds);
                break;
            }
        }
    }

    ~SelectServer()
    {
        if (_listensock < 0)
            close(_listensock);
    }

private:
    void DebugPrint()
    {
        cout << "_fd_array[]: ";
        for (int i = 0; i < NUM; i++)
        {
            if (_fd_array[i] != FD_NOME)
                cout << _fd_array[i] << " ";
        }
        cout << endl;
    }

    void HandlerEvent(const fd_set &rfds)
    {
        for (int i = 0; i < NUM; i++)
        {
            // 1.去掉不合法的fd
            if (_fd_array[i] == FD_NOME)
                continue;
            // 2.合法的不一定继续
            if (FD_ISSET(_fd_array[i], &rfds))
            {
                // 指定的fd就绪了 -> 这里是读事件
                if (_fd_array[i] == _listensock)
                {
                    // 有套接字到来了,要求连接
                    Accepter();
                }
                else
                {
                    // 有套接字到来了,需要读取数据
                    Recver(i);
                }
            }
        }
    }

    void Accepter()
    {
        string client_ip;
        uint16_t client_port = 0;

        // 连接套机子  此时不会阻塞的
        int sock = Sock::Accpet(_listensock, &client_port, &client_ip);
        if (sock < 0)
        {
            logMessage(ERROR, "accept error: %d : %s", errno, strerror(errno));
            return;
        }
        logMessage(DEBUG, "accept success");

        // 把sock放入数组中
        int pos = 1;
        for (; pos < NUM; pos++)
        {
            if (_fd_array[pos] == FD_NOME)
                break;
        }
        if (pos == NUM)
        {
            logMessage(WARNING, "%s: %d", "select server alreadly full, close: %d", sock);
            close(sock);
        }
        else
        {
            _fd_array[pos] = sock;
        }
    }

    void Recver(int pos)
    {
        logMessage(NORMAL, "message in Get IO event: %d", _fd_array[pos]);

        char buffer[1024];
        int n = recv(_fd_array[pos], buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            logMessage(NORMAL, "client[%d]# %s", _fd_array[pos], buffer);
        }
        else if (n == 0)
        {
            logMessage(NORMAL, "client[%d] quit, me too ...", _fd_array[pos]);
            close(_fd_array[pos]);
            _fd_array[pos] = FD_NOME;
        }
        else
        {
            logMessage(ERROR, "%d sock recv error, %d: %s", _fd_array[pos], errno, strerror(errno));
            close(_fd_array[pos]);
            _fd_array[pos] = FD_NOME;
        }
    }

private:
    uint16_t _port;
    int _listensock;
    int _fd_array[NUM];
    int fd_write[NUM]; // 写入
};

#endif